#region Using Statements
using System;
using System.IO;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Security;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
#endregion

namespace SkyBurner
{
    #region Pair class
    public class Pair<T1, T2>
    {
        private T1 m_First;
        public T1 First
        {
            get { return m_First; }
            set { m_First = value; }
        }

        private T2 m_Second;
        public T2 Second
        {
            get { return m_Second; }
            set { m_Second = value; }
        }

        public Pair(T1 pFirst, T2 pSecond)
        {
            m_First = pFirst;
            m_Second = pSecond;
        }
    }
    #endregion

    #region UserInputState class
    public enum UserAction
    {
        None,
        Left,
        Right,
        Up,
        Down,
        ButtonA,
        ButtonB,
        ButtonX,
        ButtonY,
        RightTrigger,
        LeftTrigger,
        RightShoulder,
        LeftShoulder,
        Start,
        Back
    }

    public class UserInputState
    {
        private class KeyMapping
        {
            private Dictionary<UserAction, Keys> m_keyMappings;

            public KeyMapping()
            {
                m_keyMappings = new Dictionary<UserAction, Keys>();
            }

            public void Add(UserAction pUserAction, Keys pKey)
            {
                m_keyMappings.Add(pUserAction, pKey);
            }

            public Keys Get(UserAction pUserAction)
            {
                return m_keyMappings[pUserAction];
            }
        }

        private float m_fDeltaX;
        private float m_fDeltaY;
        private float m_fVibration;
        private Dictionary<UserAction, bool> m_actionBag = new Dictionary<UserAction, bool>();
        private Dictionary<UserAction, bool> m_actionPressed = new Dictionary<UserAction, bool>();
        private GamePadState m_prevGamePadState;
        private KeyMapping m_keyMapping = new KeyMapping();
        private float m_fKeyboardSensibility;

        public float DeltaX
        {
            get { return m_fDeltaX; }
        }

        public float DeltaY
        {
            get { return m_fDeltaY; }
        }

        public float KeyboardSensibility
        {
            get { return m_fKeyboardSensibility; }
            set { m_fKeyboardSensibility = value; }
        }

        public UserInputState()
        {
            m_fDeltaX = 0.0f;
            m_fDeltaY = 0.0f;
            m_fVibration = 0.0f;
            m_fKeyboardSensibility = 0.05f;

            // bind a key for each user action
            m_keyMapping.Add(UserAction.Left, Keys.A);
            m_keyMapping.Add(UserAction.Right, Keys.D);
            m_keyMapping.Add(UserAction.Up, Keys.W);
            m_keyMapping.Add(UserAction.Down, Keys.S);
            m_keyMapping.Add(UserAction.ButtonA, Keys.RightShift);
            m_keyMapping.Add(UserAction.ButtonB, Keys.End);
            m_keyMapping.Add(UserAction.ButtonX, Keys.X);
            m_keyMapping.Add(UserAction.ButtonY, Keys.Y);
            m_keyMapping.Add(UserAction.RightTrigger, Keys.Enter);
            m_keyMapping.Add(UserAction.LeftTrigger, Keys.Delete);
            m_keyMapping.Add(UserAction.RightShoulder, Keys.O);
            m_keyMapping.Add(UserAction.LeftShoulder, Keys.P);
            m_keyMapping.Add(UserAction.Start, Keys.Home);
            m_keyMapping.Add(UserAction.Back, Keys.Back);

            // init action bag...
            m_actionBag.Add(UserAction.Left, false);
            m_actionBag.Add(UserAction.Right, false);
            m_actionBag.Add(UserAction.Up, false);
            m_actionBag.Add(UserAction.Down, false);
            m_actionBag.Add(UserAction.ButtonA, false);
            m_actionBag.Add(UserAction.ButtonB, false);
            m_actionBag.Add(UserAction.ButtonX, false);
            m_actionBag.Add(UserAction.ButtonY, false);
            m_actionBag.Add(UserAction.RightTrigger, false);
            m_actionBag.Add(UserAction.LeftTrigger, false);
            m_actionBag.Add(UserAction.RightShoulder, false);
            m_actionBag.Add(UserAction.LeftShoulder, false);
            m_actionBag.Add(UserAction.Start, false);
            m_actionBag.Add(UserAction.Back, false);

            // init action key pressed...
            m_actionPressed.Add(UserAction.Left, false);
            m_actionPressed.Add(UserAction.Right, false);
            m_actionPressed.Add(UserAction.Up, false);
            m_actionPressed.Add(UserAction.Down, false);
            m_actionPressed.Add(UserAction.ButtonA, false);
            m_actionPressed.Add(UserAction.ButtonB, false);
            m_actionPressed.Add(UserAction.ButtonX, false);
            m_actionPressed.Add(UserAction.ButtonY, false);
            m_actionPressed.Add(UserAction.RightTrigger, false);
            m_actionPressed.Add(UserAction.LeftTrigger, false);
            m_actionPressed.Add(UserAction.RightShoulder, false);
            m_actionPressed.Add(UserAction.LeftShoulder, false);
            m_actionPressed.Add(UserAction.Start, false);
            m_actionPressed.Add(UserAction.Back, false);
        }

        public void Reset()
        {
            m_fDeltaX = 0.0f;
            m_fDeltaY = 0.0f;
            m_fVibration = 0.0f;

            for (int i = 0; i < m_actionBag.Keys.Count; i++)
                m_actionBag[(UserAction)i] = false;
        }

        public void Process(GameTime gameTime)
        {
            GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
            KeyboardState keyboardState = Keyboard.GetState();

            if (gamePadState.IsConnected)
            {
                if (m_fVibration > 0.0f)
                {
                    GamePad.SetVibration(PlayerIndex.One, m_fVibration, m_fVibration);
                    m_fVibration -= 0.05f;
                }

                m_fDeltaX = gamePadState.ThumbSticks.Left.X;
                m_fDeltaY = gamePadState.ThumbSticks.Left.Y;

                m_actionBag[UserAction.ButtonA] = (gamePadState.Buttons.A == ButtonState.Pressed);
                m_actionBag[UserAction.ButtonB] = (gamePadState.Buttons.B == ButtonState.Pressed);
                m_actionBag[UserAction.ButtonX] = (gamePadState.Buttons.X == ButtonState.Pressed);
                m_actionBag[UserAction.ButtonY] = (gamePadState.Buttons.Y == ButtonState.Pressed);
                m_actionBag[UserAction.RightTrigger] = (gamePadState.Triggers.Right > 0.2f);
                m_actionBag[UserAction.LeftTrigger] = (gamePadState.Triggers.Left > 0.2f);
                m_actionBag[UserAction.RightShoulder] = (gamePadState.Buttons.RightShoulder == ButtonState.Pressed);
                m_actionBag[UserAction.LeftShoulder] = (gamePadState.Buttons.LeftShoulder == ButtonState.Pressed);
                m_actionBag[UserAction.Start] = (gamePadState.Buttons.Start == ButtonState.Pressed);
                m_actionBag[UserAction.Back] = (gamePadState.Buttons.Back == ButtonState.Pressed);
            }
            else
            {
                if (keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.Up)))
                {
                    if (m_fDeltaY < 1.0f)
                        m_fDeltaY += m_fKeyboardSensibility * gameTime.ElapsedGameTime.Milliseconds;
                    else
                        m_fDeltaY = 1.0f;
                }

                if (keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.Down)))
                {
                    if (m_fDeltaY > -1.0f)
                        m_fDeltaY -= m_fKeyboardSensibility * gameTime.ElapsedGameTime.Milliseconds;
                    else
                        m_fDeltaY = -1.0f;
                }

                if (keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.Right)))
                {
                    if (m_fDeltaX < 1.0f)
                        m_fDeltaX += m_fKeyboardSensibility * gameTime.ElapsedGameTime.Milliseconds;
                    else
                        m_fDeltaX = 1.0f;
                }

                if (keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.Left)))
                {
                    if (m_fDeltaX > -1.0f)
                        m_fDeltaX -= m_fKeyboardSensibility * gameTime.ElapsedGameTime.Milliseconds;
                    else
                        m_fDeltaX = -1.0f;
                }        

                m_actionBag[UserAction.ButtonA] = keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.ButtonA));
                m_actionBag[UserAction.ButtonB] = keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.ButtonB));
                m_actionBag[UserAction.ButtonX] = keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.ButtonX));
                m_actionBag[UserAction.ButtonY] = keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.ButtonY));
                m_actionBag[UserAction.RightTrigger] = keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.RightTrigger));
                m_actionBag[UserAction.LeftTrigger] = keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.LeftTrigger));
                m_actionBag[UserAction.RightShoulder] = keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.RightShoulder));
                m_actionBag[UserAction.LeftShoulder] = keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.LeftShoulder));
                m_actionBag[UserAction.Start] = keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.Start));
                m_actionBag[UserAction.Back] = keyboardState.IsKeyDown(m_keyMapping.Get(UserAction.Back));
            }
        }

        private void CheckAction(bool pActionPressed, bool pActionReleased, UserAction pUserAction)
        {
            m_actionBag[pUserAction] = false;

            if (pActionPressed)
                m_actionPressed[pUserAction] = true;

            if (pActionReleased)
            {
                if (m_actionPressed[pUserAction])
                {
                    m_actionBag[pUserAction] = true;
                    m_actionPressed[pUserAction] = false;
                }
            }
        }

        private void CheckKeyboardAction(KeyboardState pKeyboardState, UserAction pUserAction)
        {
            CheckAction(pKeyboardState.IsKeyDown(m_keyMapping.Get(pUserAction)), 
                        pKeyboardState.IsKeyUp(m_keyMapping.Get(pUserAction)), 
                        pUserAction);
        }

        public void ProcessEx(GameTime gameTime)
        {
            GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
            KeyboardState keyboardState = Keyboard.GetState();

            if (gamePadState.IsConnected)
            {
                float fThumbSticksValue = 0.5f;

                CheckAction((gamePadState.ThumbSticks.Left.X <= -fThumbSticksValue), (gamePadState.ThumbSticks.Left.X == 0.0f), UserAction.Left);
                CheckAction((gamePadState.ThumbSticks.Left.X >= fThumbSticksValue), (gamePadState.ThumbSticks.Left.X == 0.0f), UserAction.Right);
                CheckAction((gamePadState.ThumbSticks.Left.Y >= fThumbSticksValue), (gamePadState.ThumbSticks.Left.Y == 0.0f), UserAction.Up);
                CheckAction((gamePadState.ThumbSticks.Left.Y <= -fThumbSticksValue), (gamePadState.ThumbSticks.Left.Y == 0.0f), UserAction.Down);
                CheckAction((gamePadState.Buttons.A == ButtonState.Pressed), (gamePadState.Buttons.A != ButtonState.Pressed), UserAction.ButtonA);
                CheckAction((gamePadState.Buttons.B == ButtonState.Pressed), (gamePadState.Buttons.B != ButtonState.Pressed), UserAction.ButtonB);
                CheckAction((gamePadState.Buttons.X == ButtonState.Pressed), (gamePadState.Buttons.X != ButtonState.Pressed), UserAction.ButtonX);
                CheckAction((gamePadState.Buttons.Y == ButtonState.Pressed), (gamePadState.Buttons.Y != ButtonState.Pressed), UserAction.ButtonY);
                CheckAction((gamePadState.Buttons.Start == ButtonState.Pressed), (gamePadState.Buttons.Start != ButtonState.Pressed), UserAction.Start);
                CheckAction((gamePadState.Buttons.Back == ButtonState.Pressed), (gamePadState.Buttons.Back != ButtonState.Pressed), UserAction.Back);
                CheckAction((gamePadState.Buttons.RightShoulder == ButtonState.Pressed), (gamePadState.Buttons.RightShoulder != ButtonState.Pressed), UserAction.RightShoulder);
                CheckAction((gamePadState.Buttons.LeftShoulder == ButtonState.Pressed), (gamePadState.Buttons.LeftShoulder != ButtonState.Pressed), UserAction.LeftShoulder);

                m_prevGamePadState = gamePadState;
            }
            else
            {
                CheckKeyboardAction(keyboardState, UserAction.Left);
                CheckKeyboardAction(keyboardState, UserAction.Right);
                CheckKeyboardAction(keyboardState, UserAction.Up);
                CheckKeyboardAction(keyboardState, UserAction.ButtonA);
                CheckKeyboardAction(keyboardState, UserAction.ButtonB);
                CheckKeyboardAction(keyboardState, UserAction.ButtonX);
                CheckKeyboardAction(keyboardState, UserAction.ButtonY);
                CheckKeyboardAction(keyboardState, UserAction.Start);
                CheckKeyboardAction(keyboardState, UserAction.Back);
                CheckKeyboardAction(keyboardState, UserAction.RightShoulder);
                CheckKeyboardAction(keyboardState, UserAction.LeftShoulder);
            }
        }

        public bool IsUserAction(UserAction pAction)
        {
            return m_actionBag[pAction];
        }

        public void Rumble(float fIntensity)
        {
            m_fVibration = fIntensity;
        }
    }
    #endregion
}
